﻿#include "bonus.h"

BonusManager bonus_manager;

BonusManager::BonusManager() :
bonuses(0), id_index_table(0), active_bonus_count(0), next_free_id_slot(0)
{
	//bonuses.reserve(MAX_BONUS);
	bonuses = new Bonus[MAX_BONUS];
	id_index_table=new unsigned int[MAX_BONUS];
	for(unsigned int t=0;t<MAX_BONUS;++t) id_index_table[t]=t+1;
}

BonusManager::~BonusManager()
{
	//bonuses.clear();
	delete[] bonuses;
	delete[] id_index_table;
}

void BonusManager::Init(float b_accel, float max_s, float s_time, float p_line, int p_score_min, int p_score_max)
{
	bonus_accel = b_accel;
	max_speed = max_s;
	score_time = s_time;
	point_line = p_line;
	point_score_min = p_score_min;
	point_score_max = p_score_max;
	bonus_sprite = sprite_container.GetSprite("bonuses");
}

unsigned int BonusManager::Add(Bonus &p_bonus_description)
{
	Bonus &b = bonuses[active_bonus_count];
	b = p_bonus_description;
	b.active = 1;
	b.collision_size = 9;
	b.time_lived = 0;
	if (b.bonus_type == 16)
		b.to_player = 1;
	else
		b.to_player = 0;
	unsigned int l_id=next_free_id_slot;
	b.id = l_id;
	next_free_id_slot=id_index_table[l_id];
	id_index_table[l_id]=active_bonus_count++;
	return l_id;
}

void BonusManager::Clear()
{
	active_bonus_count = 0;
	next_free_id_slot = 0;
	for(unsigned int t=0;t<MAX_BONUS;++t) id_index_table[t]=t+1;
}

void BonusManager::Update(float time)
{
	unsigned int l_new_index=0;
	for(unsigned int t=0;t<active_bonus_count;++t)
	{
		bonuses[t].time_lived += time;
		bonuses[t].prev_position = bonuses[t].position;
		if (player.position.y < point_line)
			bonuses[t].to_player = 1;
		if (player.is_bomb || bonuses[t].to_player)
		{
			if (player.is_bomb && (bonuses[t].speed < (max_speed * 6.0f)) || bonuses[t].to_player == 1)
			{
				bonuses[t].speed = max_speed * 6.0f;
				bonuses[t].to_player = 1;
			}
			bonuses[t].speed += bonus_accel * 4.0f * time;
			bonuses[t].position += (player.position - bonuses[t].position).normalize() * bonuses[t].speed * time;
		}
		else
		{
			bonuses[t].speed += bonus_accel * time;
			if (bonuses[t].speed > max_speed)
				bonuses[t].speed = max_speed;
			bonuses[t].move.y = bonuses[t].speed;
			if (bonuses[t].speed > 0)
				bonuses[t].move.x = 0;
			bonuses[t].position += bonuses[t].move * time;
			if (bonuses[t].position.y > global_state.game_field.bottom)
				bonuses[t].active = 0;
		}
		// Удаляем ушедших за экран
		if (!bonuses[t].active)
		{
			id_index_table[bonuses[t].id] = next_free_id_slot;
			next_free_id_slot = bonuses[t].id;
		}
		else
		{
			if(t!=l_new_index)
			{
				bonuses[l_new_index]=bonuses[t];
				id_index_table[bonuses[t].id]=l_new_index;
			}
			l_new_index++;
		}
	}
	active_bonus_count = l_new_index;
	// Удаляем ушедших за экран
	//t = 0;
	//while (t < bonuses.size())
	//	if (bonuses[t].active)
	//		t++;
	//	else
	//		bonuses.erase(bonuses.begin() + t);
}

void BonusManager::Render(CL_GraphicContext &gc, CL_Rect &clip)
{
	gc.set_cliprect(clip);
	for(unsigned int t=0;t<active_bonus_count;++t)
	{
		if ((bonuses[t].position.y + 16) < global_state.render_field.top && bonuses[t].bonus_type != 16)
		{
			// Бонус улетел за экран, вывести вместо него указатель
			bonus_sprite->set_alpha(0.5);
			bonus_sprite->set_frame(bonuses[t].bonus_type + 8);
			bonus_sprite->draw(gc, bonuses[t].position.x - 8, global_state.render_field.top);
		}
		else
		{
			if (bonuses[t].speed < -0.1)
			{
				// повертим выбитый бонус
				bonus_sprite->set_angle(CL_Angle(bonuses[t].time_lived, cl_degrees));
			}
			else
				bonus_sprite->set_angle(CL_Angle(0,cl_degrees));
			bonus_sprite->set_alpha(1.0);
			bonus_sprite->set_frame(bonuses[t].bonus_type);
			bonus_sprite->set_alignment(origin_center);
			bonus_sprite->draw(gc, bonuses[t].position.x, bonuses[t].position.y);
		}
	}
	gc.reset_cliprect();
}

void BonusManager::CalculateCollisions()
{
	size_t t;
	GameObject t_player;
	t_player.position = player.position;
	t_player.prev_position = player.prev_position;
	for(unsigned int t=0;t<active_bonus_count;++t)
	{
		if (player.focused)
			t_player.collision_size = player.bonus_attract_focus_collision;
		else
			t_player.collision_size = player.bonus_attract_collision;
		if (bonuses[t].Collide(&t_player))
			bonuses[t].to_player = 2;
	}
	t_player.collision_size = player.bonus_collision_size;
	unsigned int l_new_index=0;
	for(unsigned int t=0;t<active_bonus_count;++t)
	{
		if (bonuses[t].Collide(&t_player))
		{
			// выдать плюшки игроку
			int add_score = 10;
			Effect bonus_effect;
			bonus_effect.appearance_index = 0;
			switch(bonuses[t].bonus_type)
			{
			case 0:
				// паверап
				if (player.power == 128)
					add_score = 1000;
				else
				{
					add_score = 10;
					player.power++;
				}
				smanager.PlaySound("item00");
				break;
			case 1:
				// очки
				if (bonuses[t].position.y < point_line) // Линия сбора
				{
					add_score = point_score_max;
					bonus_effect.appearance_index = 1;
				}
				else
					add_score = point_score_min + (((global_state.render_size.height - point_line) - (bonuses[t].position.y - point_line)) / (global_state.render_size.height - point_line)) * (point_score_max - point_score_min);
				player.points++;
				smanager.PlaySound("item00");
				break;
			case 2:
				// время
				// прибавляет 1 секунду ко времени спеллкарты
				add_score = 1024;
				global_state.time_collected++;
				player.spell_time += 1000;
				player.spell_time_remaining += 1000;
				if (player.spell_time_remaining > player.spellcard_max_duration)
					player.spell_time_remaining = player.spellcard_max_duration;
				if (player.spell_time > player.spellcard_max_duration)
				{
					if (player.spells < 8)
					{
						player.spell_time = 1000;
					}
					else
					{
						// до 8 заряженных спеллкарт одновременно
						add_score = 5000;
						player.spell_time = player.spellcard_max_duration;
					}
				}
				if (player.spell_time == player.spellcard_min_duration)
				{
					// Набрали времени на новый спелл
					player.spells++;
					player.spell_time_remaining = player.spellcard_min_duration;
					smanager.PlaySound("powerup");
				}
				else
					smanager.PlaySound("item01");
				break;
			case 3:
				// Большой паверап
				if (player.power == 128)
					add_score = 10000;
				else
				{
					add_score = 1000;
					player.power += 8;
					if (player.power > 128)
						player.power = 128;
				}
				smanager.PlaySound("powerup");
				break;
			case 16:
				// Star Item
				add_score = 300;
				bonus_effect.appearance_index = 2;
				smanager.PlaySound("item00");
				break;
			}
			player.score += add_score;
			// запустить эффектом циферок количество очков
			bonus_effect.position = bonuses[t].position;
			bonus_effect.param_int = add_score;
			bonus_effect.speed = score_time;
			bonus_effect.appearance = renders.GetEffectRender("score_float");
			bonus_effect.behaviour = patterns.GetEffectBehaviour("score_float");
			effects.Create(bonus_effect);
			// удалить бонус
			bonuses[t].active = 0;
		}
		// Убираем сработавших
		if (!bonuses[t].active)
		{
			id_index_table[bonuses[t].id] = next_free_id_slot;
			next_free_id_slot = bonuses[t].id;
		}
		else
		{
			if(t!=l_new_index)
			{
				bonuses[l_new_index]=bonuses[t];
				id_index_table[bonuses[t].id]=l_new_index;
			}
			l_new_index++;
		}
	}
	active_bonus_count = l_new_index;
	// Убираем сработавших
	//t = 0;
	//while (t < bonuses.size())
	//	if (bonuses[t].active)
	//		t++;
	//	else
	//		bonuses.erase(bonuses.begin() + t);
}

